/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id: mac_parse.c 348 2010-11-29 09:01:16Z bgao $ 
#
# $Author: bgao $ 
#
# $Revision: 348 $ 
#
# $Date: 2010-11-29 17:01:16 +0800 (星期一, 29 十一月 2010) $ 
#
# Description: 
#
=============================================================================*/
/*! @file mac_parse.c
 *  @brief This file implented the MAC frame parser.
 */
#include <string.h>
#include <stdio.h>
#include "mac.h"

static uint8 bic(uint8 x, uint8 m);

/*! parse frame control field.
 *  @param value the binary value of frame control field.
 *  @param ctrl pointer to the frame control structure.
 *  @return -1 if error occurs else return 0.
 */
uint8 mac_parse_frm_ctrl(uint16 value, mac_frm_ctrl_t *ctrl)
{
    ctrl->frm_type     = (uint8)((value & MAC_FRM_TYPE_MASK)    >> MAC_FRM_TYPE_OFFSET);
    ctrl->en_sec       = (uint8)((value & MAC_EN_SEC_MASK)      >> MAC_EN_SEC_OFFSET);
    ctrl->frm_pend     = (uint8)((value & MAC_FRM_PEND_MASK)    >> MAC_FRM_PEND_OFFSET);
    ctrl->ack          = (uint8)((value & MAC_ACK_MASK)         >> MAC_ACK_OFFSET);
    ctrl->pan_id_comp  = (uint8)((value & MAC_PAN_ID_COMP_MASK) >> MAC_PAN_ID_COMP_OFFSET);
    ctrl->dest_addr_m  = (uint8)((value & MAC_DEST_ADDR_M_MASK) >> MAC_DEST_ADDR_M_OFFSET);
    ctrl->frm_ver      = (uint8)((value & MAC_FRM_VER_MASK)     >> MAC_FRM_VER_OFFSET);
    ctrl->src_addr_m   = (uint8)((value & MAC_SRC_ADDR_M_MASK)  >> MAC_SRC_ADDR_M_OFFSET);

    return 0;
}

/*! parse superframe field.
 *  @param value the binary value of superframe field.
 *  @param sup_frm pointer to the superframe structure.
 *  @return -1 if error occurs else return 0.
 */
uint8 mac_parse_sup_frm(uint16 value, mac_sup_frm_t *sup_frm)
{
    sup_frm->bcn_order     = (uint8)((value & MAC_BCN_ORDER_MASK)     >> MAC_BCN_ORDER_OFFSET);
    sup_frm->sup_frm_order = (uint8)((value & MAC_SUP_FRM_ORDER_MASK) >> MAC_SUP_FRM_ORDER_OFFSET);
    sup_frm->fnl_cap_slot  = (uint8)((value & MAC_FNL_CAP_SLOT_MASK)  >> MAC_FNL_CAP_SLOT_OFFSET);
    sup_frm->ble           = (uint8)((value & MAC_BLE_MASK)           >> MAC_BLE_OFFSET);
    sup_frm->pan_coord     = (uint8)((value & MAC_PAN_COORD_MASK)     >> MAC_PAN_COORD_OFFSET);
    sup_frm->assoc_pmit    = (uint8)((value & MAC_ASSOC_PMIT_MASK)    >> MAC_ASSOC_PMIT_OFFSET);

    return 0;
}

/*! parse capability information field.
 *  @param value the binary value of capability information field.
 *  @param cap_info pointer to the capability information structure.
 *  @return -1 if error occurs else return 0.
 */
uint8 mac_parse_cap_info(uint8 value, mac_cap_info_t *cap_info)
{
    cap_info->altnt_pan_coord   =  (uint8)((value & ALTNT_PAN_COORD_MASK)   >>  ALTNT_PAN_COORD_OFFSET);
    cap_info->dev_type          =  (uint8)((value & DEV_TYPE_MASK)          >>  DEV_TYPE_OFFSET);
    cap_info->pw_src            =  (uint8)((value & PW_SRC_MASK)            >>  PW_SRC_OFFSET);
    cap_info->rec_on_when_idle  =  (uint8)((value & REC_ON_WHEN_IDLE_MASK)  >>  REC_ON_WHEN_IDLE_OFFSET);
    cap_info->sec_cap           =  (uint8)((value & SEC_CAP_MASK)           >>  SEC_CAP_OFFSET);
    cap_info->alloc_addr        =  (uint8)((value & ALLOC_ADDR_MASK)        >>  ALLOC_ADDR_OFFSET);

    return 0;
}

/*! parse beacon request frame.
 *  @param buf pointer to the structure used to store the generated frame.
 *  @param hdr pointer to the mac header structure.
 *  @return -1 if error occurs else return 0.
 */
int8 mac_parse_hdr(buf_t *buf, mac_hdr_t *hdr)
{
    uint8 frm_len = 0;
    uint16 frm_ctrl_value = 0;
    mac_frm_ctrl_t *ctrl = &hdr->ctrl;
    addr_t *dest = &hdr->dest_addr;
    addr_t *src = &hdr->src_addr;

    if (buf == NULL || buf->ptr == NULL) {
        return -1;
    }

    memset(hdr, 0, sizeof(mac_hdr_t));

    frm_len = *(uint8 *)buf->ptr;
    MAC_DEBUG("mac_parse_hdr: length of the buffer is %d\n", frm_len);
    buf->ptr++;

    frm_ctrl_value = *(uint16 *)buf->ptr;
    buf->ptr += 2;
    mac_parse_frm_ctrl(frm_ctrl_value, ctrl);
    MAC_DEBUG("\nmac_parse_hdr:****ctrl->pan_id_comp is  %d\n", ctrl->pan_id_comp);

    hdr->dsn = *(uint8 *)buf->ptr;
    buf->ptr++;

    if (ctrl->dest_addr_m != NOT_PRESENT) {
        hdr->dest_pan_id = *(uint16 *)buf->ptr;
        buf->ptr += 2;
    }

    if (ctrl->dest_addr_m == ADDR_16_M) {
        dest->mode = ADDR_16_M;
        dest->a.addr16 = *(uint16 *)buf->ptr;
        buf->ptr += 2;
    } else if (ctrl->dest_addr_m == ADDR_64_M) {
        dest->mode = ADDR_64_M;
        dest->a.addr64 = *(uint64 *)buf->ptr;
        MAC_DEBUG("mac_parse_hdr****dest->a.addr64 is 0x%x\n", (uint32)dest->a.addr64);
        buf->ptr += 8;
    }

    MAC_DEBUG("\nmac_parse_hdr****ctrl->pan_id_comp is %d\n", ctrl->pan_id_comp);
    if (ctrl->pan_id_comp == 0 &&
        ctrl->src_addr_m != NOT_PRESENT) {
        hdr->src_pan_id = *(int8 *)buf->ptr;
        MAC_DEBUG("mac_parse_hdr****hdr->src_pan_id is 0x%x\n", hdr->src_pan_id);
        buf->ptr += 2;
    }

    if (ctrl->src_addr_m == ADDR_16_M) {
        src->mode = ADDR_16_M;
        src->a.addr16 = *(uint16 *)buf->ptr;
        MAC_DEBUG("mac_parse_hdr****src->a.addr16 is 0x%x\n", src->a.addr16);
        buf->ptr += 2;
    } else if (ctrl->src_addr_m == ADDR_64_M) {
        src->mode = ADDR_64_M;
        src->a.addr64 = *(uint64 *)buf->ptr;
        MAC_DEBUG("mac_parse_hdr****src->a.addr64 is 0x%x\n", (uint32)src->a.addr64);
        buf->ptr += 8;
    }

    return 0;
}

/*! parse the FCS of field.
 *  @param buf pointer to the structure used to store the frame.
 *  @param fcs pointer to FCS structure.
 *  @return return 0 if ok.
 */
int8 mac_parse_fcs(buf_t *buf, mac_fcs_t *fcs)
{
    /* get the pointer to the first byte of fcs */
    uint8 *ptr = &buf->array[aMaxPHYPacketSize-1];
    memset(fcs, 0, sizeof(mac_fcs_t));

    fcs->rssi = *(uint8 *)ptr;
    ptr++;

    /* the following info is paded by radio automatically. */
    fcs->corr_value = *(uint8 *)ptr;
    fcs->is_crc_ok = fcs->corr_value & 0x80;
    fcs->corr_value = bic(fcs->corr_value, 1<<7);

    return 0;
}

/*! clear bits of x according to m, if the corresponding bit in m is 1,
 *  then clear it to 0.
 *  @param x the byte to be cleared.
 *  @param m the pattern to follow when clearing.
 *  @return return the result. 
 */
static uint8 bic(uint8 x, uint8 m)
{
    return x & ~m;
}

/*! parse the beacon frame.
 *  @param buf pointer to the structure used store the beacon frame.
 *  @param bcn pointer to the structure of beacon frame.
 *  @return return 0 if ok.
 *  TODO include the beacon payload parsing.
 */
int8 mac_parse_bcn(buf_t *buf, mac_frm_bcn_t *bcn)
{
    bcn->sup_frm_value = *(uint16 *)buf->ptr;
    buf->ptr += 2;

    return 0;
}

/*! parse the association request frame
 *  @param buf pointer to the structure used to store the frame.
 *  @param req pointer to the mac association request frame structure.
 *  @return return 0 if ok.
 */
int8 mac_parse_assoc_req(buf_t *buf, mac_frm_assoc_req_t *req)
{
    req->cap_info = *(uint8 *)buf->ptr;
    MAC_DEBUG("mac_parse_assoc_req: value of cap_info is 0x%x\n", *(uint8 *)buf->ptr);
    buf->ptr++;

    return 0;
}

/*! parse the command frame, get the frame type.
 *  this function should be called after mac_parse_hdr and after specific command parser.
 *  @param buf pointer to the stucture used to store the frame.
 *  @return return the id of the command.
 */
uint8 mac_parse_cmd(buf_t *buf)
{
    uint8 cmd_id = *(uint8 *)(buf->ptr++);
    
    return cmd_id;
}

/*! parse the association response command.
 *  @param buf pointer to the structure used to store the frame.
 *  @param resp pointer to the association response structure.
 *  @return return 0 if ok.
 */
int8 mac_parse_assoc_resp(buf_t *buf, mac_frm_assoc_resp_t *resp)
{
    /* copy the short address */
    resp->short_addr = *(uint16 *)buf->ptr;
    buf->ptr += 2;
    
    resp->status = *(uint8 *)buf->ptr;
    buf->ptr++;

    return 0;
}

